#include"myshell.h"
#include<cstdio>
#include<stdlib.h>
#include<string>
#include<iostream>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>


//将解析出来的命令放入全局参数列表中以让子进程拿到
int gargc = 0;
char *gargv[ARGS] = {NULL};
char pwd[4096];
int lastcode = 0;//上一个程序的退出码信息

#define NONE_REDIR 0
#define OUTPUT_REDIR 1
#define APPEND_REDIR 2
#define INPUT_REDIR 3

std::string filename;//如果要重定向记录需要重定向的文件
int redir_type = NONE_REDIR;//默认没有重定向

static std::string GetUserName()
{
    std::string username = getenv("USER");
    return username.empty() ? "None":username;
}

static std::string GetHostName()
{
    std::string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

static std::string GetPwd()
{
    //std::string pwd = getenv("PWD");
    //环境变量的变化，可能会依赖于进程，pwd 需要shell 自己进行更新
    //此处使用系统调用来获取pwd,然后主动更新环境变量中的pwd
    char temp[1024];
    getcwd(temp , sizeof(temp));//利用系统调用获取
    //使用putenv ， 可以更新环境变量但是需要注意的是
    //字符串的格数，kv结构，可以借助于snprintf
    snprintf(pwd , sizeof(pwd) , "PWD=%s", temp);//转换格式
    putenv(pwd);//刷新环境变量中的pwd
    
    //将打印出来的当前工作路径进行截断，利用rfind
    std::string pwd_lable = temp;
    const std::string pathsep = "/";
    auto pos = pwd_lable.rfind(pathsep);
    if(pos == std::string::npos)
    {
        return "None";
    }
    pwd_lable = pwd_lable.substr(pos + pathsep.size());
    return pwd_lable.empty() ? "/" : pwd_lable ;//根目录进行特殊处理
    //return temp;
    //return pwd.empty() ? "None" : pwd;
}

static std::string GetHomePath()
{
    std::string home = getenv("HOME");
    return home.empty() ? "None" : home;
}

static std::string GetOldPwd()
{
    std::string oldpwd =  getenv("OLDPWD");
    return oldpwd.empty() ? "None" : oldpwd;
}
//输出命令行提示符
void PrintCommandPrompt()
{
    //[用户名@主机名 当前工作路径]#
    //从环境变量中获取这些信息，也可以使用系统调用
    std::string user = GetUserName();
    std::string hostname = GetHostName();
    std::string pwd = GetPwd();
    printf("[%s@%s %s]# " , user.c_str() , hostname.c_str() , pwd.c_str());
}

//获取用户输入的命令行字符串
bool GetCommandString(char cmd_str_buff[] , int len)
{
    //从标准输入中获取数据
    if(cmd_str_buff == NULL || len <=0) return false;
    char* res = fgets(cmd_str_buff, len ,stdin);
    if(res == NULL) return false;

    //cmd_str_buff 一定不会为空，因为至少会按下一个enter
    //需要处理在键盘上按下的\n ， 让commandstr 更加纯粹
    cmd_str_buff[strlen(cmd_str_buff)-1] = 0;
    return strlen(cmd_str_buff) == 0 ? false : true;//为空串则不执行
}

//将用户输入的命令行字符串进行解析，并放入全局的命令行参数列表中
bool ParseCommandString(char cmd[])
{
    //利用strtok 进行分割
    if(cmd == NULL) return false;
//可以在函数中定义宏
#define SEP " "
    gargv[gargc++] = strtok(cmd ,SEP);
    //除了第一次使用strtok 需要传递具体所要分割的字符串
    //后面使用的时候第一个参数传NULL就可以了，strtok 内部会自动记录
    //当strtok 分割完的时候会返回0，表达式的返回值是左值
    while((bool)(gargv[gargc++] = strtok(NULL , SEP)));
    //因为最后一次strtok 读取为0，gargc 也记录了一次，所以需要回退一次
    gargc--;

    //做一下debug
//#define DEBUG
#ifdef DEBUG
    printf("gargc:%d\n" , gargc);
    printf("----------------\n");
    for(int i = 0; i< gargc;i++)
    {
        printf("gargv[%d]:%s\n" ,i , gargv[i]);
    }
    printf("----------------\n");
    for(int i = 0; gargv[i];i++)
    {

        printf("gargv[%d]:%s\n" ,i , gargv[i]);
    }


#endif

    return true;
}

void InitGlobal()
{
    gargc = 0;
    memset(gargv , 0 ,sizeof(gargv));
    filename.clear();
    redir_type = NONE_REDIR;
}

void ForkAndExec()
{
    //子进程拿到父进程的gargc gargv , fork + execvp 程序替换便可
    //父进程等待子进程
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return;
    }
    else if(id == 0)
    {
        //打开文件 + dup2
        if(redir_type == OUTPUT_REDIR)//输出重定向 output 1
        {
            int output = open(filename.c_str() , O_CREAT | O_TRUNC | O_WRONLY , 0666);
            if(output <0 )
            {
                perror("open");
                exit(2);
            }
            dup2(output , 1);
        }
        else if(redir_type == INPUT_REDIR )//输入重定向
        {
            int input = open(filename.c_str() , O_RDONLY);
            if(input < 0)
            {
                perror("open");
                exit(2);
            }
            dup2(input , 0);
        }
        else if(redir_type == APPEND_REDIR)//追加重定向
        {
            int append = open(filename.c_str() ,O_CREAT | O_WRONLY | O_APPEND , 0666);
            if(append < 0)
            {
                perror("open");
                exit(2);
            }
            dup2(append , 1);
        }
        else
        {
            //没了：
        }
        //程序替换
        execvp(gargv[0] , gargv);
        exit(1);
    }
    //父进程
    int status = 0;
    int rid = waitpid(id , &status , 0);
    if(rid > 0)
    {
        //等待子进程成功,获取子进程的退出码信息
        lastcode = WEXITSTATUS(status);
    }
}

bool BuiltCommandExec()
{
    //判断gargv[0] 是否为内建命令
   bool ret = false;
   std::string cmd = gargv[0];
   if(cmd == "cd")
   {
       //让父进程更改路径，使用 chdir
        if(gargc == 2)
        {
            //cd - , cd ` , cd 目录
            std::string target = gargv[1];
            if(target == "~")
            {
                ret = true;
                chdir(GetHomePath().c_str());

            }
            else if(target == "-")
            {
                ret = true;
                chdir(GetOldPwd().c_str());
            }
            else
            {
                ret = true;
                chdir(gargv[1]);
            }
        }
        else if(gargc ==1)
        {
            ret = true;
            //回到家目录
            chdir(GetHomePath().c_str());

        }
        else
        {
            //bug
        }
   }
   else if(cmd == "echo")
   {
       if(gargc==2)
       {
           std::string args = gargv[1];
          // if(args == "$?")
          // {
          //     printf("lastcode:%d\n" , lastcode);
          //     lastcode = 0;
          //     ret = true;
          // }
          if(args[0] == '$')
          {
              if(args[1] == '?')
              {
                  printf("lastcode:%d\n" , lastcode);
                  lastcode = 0;
                  ret = true;
              }
              else
              {
                  const char* name = &args[1];
                  printf("%s\n" , getenv(name));
                  lastcode = 0;
                  ret = true;
              }
          }
       }
       else
       {
           printf("%s\n" , gargv[1]);
           ret = true;
       }
   }
   return ret;
}

#define TrimSpace(start) do{\
        while(isspace(*start))\
    {\
        start++;\
    }\
}while(0);

void CheckRedir(char cmd[])
{
    //直接遍历判断即可
    char* start = cmd;
    char* end = cmd + strlen(cmd)-1;

    //遍历查找
    while(start <= end)
    {
        if(*start == '>')
        {
            if(*(start+1) == '>')
            {
                //追加重定向
                redir_type = APPEND_REDIR;
                //将命令行切割为两截，前一截可以继续交给后面解决；而后一截中就包含了所要重定向的文件，去除空格然后获取
                *start = '\0';
                start += 2;
                TrimSpace(start);//去除空格
                filename = start;
                break;
            }
            else
            {
                //输出重定向
                redir_type = OUTPUT_REDIR;
                *start = '\0';
                start++;
                TrimSpace(start);
                filename = start;
                break;
            }
        }
        else if(*start == '<')
        {
            //输入重定向
            redir_type = INPUT_REDIR;
            *start = '\0';
            start++;
            TrimSpace(start);
            filename = start;
            break;
        }
        else
        {
            start++;
        }
    }
}
